#pragma once

#include <cstdint>
#include <memory>
#include <vector>
#include <string>
#include <functional>

#include "IFourierTransformStructures.h"
#include "ImportExport.h"

namespace FTLib
{
	class IRealToComplex_PackedFftwComplex
	{
	public:
		virtual void RealToComplex_PackedFftwComplex(const FourierTransfRealInputData2d& input, const FourierTransfComplexOutput2d& output, const FourierTransformAdditional* pAdditional) = 0;
	};

	class IRealToComplex_PackedIppComplex
	{
	public:
		virtual void RealToComplex_PackedIppComplex(const FourierTransfRealInputData2d& input, const FourierTransfComplexOutput2d& output, const FourierTransformAdditional* pAdditional) = 0;
	};

	class IRealToComplex_Complex
	{
	public:
		virtual void RealToComplex_Complex(const FourierTransfRealInputData2d& input, const FourierTransfComplexOutput2d& output, const FourierTransformAdditional* pAdditional) = 0;
	};

	class IComplexToComplexFwd_Complex
	{
	public:
		virtual void ComplexToComplex_Complex(const FourierTransfComplexInput2d& input, const FourierTransfComplexOutput2d& output, const FourierTransformAdditional* pAdditional) = 0;
	};

	class IComplexToComplexInv_Complex
	{
	public:
		virtual void ComplexToComplex_Complex(const FourierTransfComplexInput2d& input, const FourierTransfComplexOutput2d& output, const FourierTransformAdditional* pAdditional) = 0;
	};

	class IComplexHermitianToReal
	{
	public:
		virtual void ComplexHermitianToReal(const FourierTransfComplexInput2d& input, const FourierTransfRealOutputData2d& output, const FourierTransformAdditional* pAdditional) = 0;
	};

	class IFourierTransformFactory
	{
	public:
		struct Options
		{
			Options() : width(0), height(0), normalizationFactor(1) {};
			uint32_t		width;
			uint32_t		height;

			/// <summary>	
			/// The normalization factor. 
			/// +Infinity means - normalize by sqrt of size (=width*height).
			/// -Infinity means - normalize by size (=width*height).
			/// </summary>
			double			normalizationFactor;
		};

		/// <summary>	Creates an engine which performs a Fourier-transformation.
		/// 			The input is given as a real bitmap, the output are the 
		/// 			complex Fourier-coefficients in	FFTW-packed-format.
		/// 			The result is a hermetian matrix.
		/// 			The size of the output is a (complex valued) bitmap of size (width/2+1) x h.
		/// </summary>
		/// <param name="opts">	Options for controlling the operation. </param>
		/// <returns>	The new real-to-complex-packed-in-fftw-format FFT engine. </returns>
		virtual std::shared_ptr<IRealToComplex_PackedFftwComplex> CreateRealToComplexPackedFftw(const Options& opts) = 0;

		virtual std::shared_ptr<IRealToComplex_PackedIppComplex> CreateRealToComplexPackedIpp(const Options& opts) = 0;

		virtual std::shared_ptr<IRealToComplex_Complex> CreateRealToComplex(const Options& opts) = 0;

		virtual std::shared_ptr<IRealToComplex_Complex> CreateRealToComplex_Shifted(const Options& opts) = 0;

		virtual std::shared_ptr<IComplexToComplexFwd_Complex> CreateComplexToComplexFwd(const Options& opts) = 0;

		virtual std::shared_ptr<IComplexToComplexInv_Complex> CreateComplexToComplexInv(const Options& opts) = 0;

		/// <summary>	Creates an engine which performs an inverse Fourier-transformation. 
		/// 			The input is a complex matrix, and this matrix is assumed to
		/// 			be hermitian. The output is therefore a real matrix.
		/// </summary>
		/// <param name="opts">	Options for controlling the operation.</param>
		/// <returns>	The new complex-hermitian-to-real FFT engine. </returns>
		virtual std::shared_ptr<IComplexHermitianToReal> CreateComplexHermitianToReal(const Options& opts) = 0;
	};

	enum class FourierTransformEngines
	{
		CuFft,
		Ipp,
		FFTW,
		D3DCSX,
		CLFFT
	};

	bool FTLIB_API InitializeFTLib(std::function<const wchar_t*(void)> funcEnumAdditionalDllsFolders);

	void FTLIB_API ShutdownFTLib();

	std::shared_ptr<IFourierTransformFactory> FTLIB_API CreateFourierTransformEngine(FourierTransformEngines type, const wchar_t* initializationArguments = nullptr);

	std::vector<FourierTransformEngines> FTLIB_API GetListOfEngines();

	bool FTLIB_API IsEngineAvailable(FourierTransformEngines type);

	enum class EngineInfoType
	{
		Name,
		Version
	};

	void FTLIB_API GetEngineInfo(EngineInfoType infoType, FourierTransformEngines type, std::wstring& info);

	std::shared_ptr<IPerformanceData> FTLIB_API CreatePerformanceData();
}